﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Threading;
using System.Text.RegularExpressions;
using System.IO;

namespace Rsdn.Humor.Collector
{
  static class Helpers
  {
    private static readonly Action<DispatcherFrame> _CompleteFrame = (f) => f.Continue = false;
    public static void DoEvents(this Dispatcher disp)
    {
      DispatcherFrame frame = new DispatcherFrame();
      disp.BeginInvoke(_CompleteFrame, DispatcherPriority.ApplicationIdle, frame);
      Dispatcher.PushFrame(frame);
    }
  }

  public delegate void ProgressHandlerDelegate(double pc);
  static class ResReqHelper
  {
    private const string _StatDataFile = "stat.dat";
    private const string _OneOrMoreSpace = @"\s+";
    private const string _ZeroOrMoreSpace = @"\s*";
    private const string _Quote = @"(\""|\')";
    private const string _ZeroOrOneQuote = _Quote + @"?";
    private const string _Reference = @"((\/|\.|\#|\w|\d)+)?";
    private const string _Value = @"((?!(\<|\>))(\w|\W))*";    //@"(\""|\'|\=|\/|\.|\#|\w|\d|\s|\:|\(|\)|\-|\;|\%|\&)*";
    private const RegexOptions _PatternsOpt = RegexOptions.CultureInvariant | RegexOptions.Compiled;
    private static Statistics _Statistics;

    private static int _AddedResReq = 0;
    private static int _CompletedResReq = 0;
    private static double _LastPc = 0;

    private static readonly string _LB = _ZeroSpace(@"\<");
    private static readonly string _RB = _ZeroSpace(@"\>");

    private static string _ZeroSpace(string el)
    {
      return string.Format(@"{0}{1}", _ZeroOrMoreSpace, el);
    }

    private static string _OneSpace(string el)
    {
      return string.Format(@"{0}{1}", _OneOrMoreSpace, el);
    }

    private static string _GetTagAttributePattern(string attr, string value)
    {
      return _OneSpace(attr) + _ZeroSpace(@"\=") + _ZeroSpace(_ZeroOrOneQuote) + value + _ZeroSpace(_ZeroOrOneQuote);
    }

    private static string _GetTagStartPattern(string tag, params string[] attrs)
    {
      var ret = new StringBuilder();
      ret.Append(_LB);
      ret.Append(_ZeroSpace(tag));
      if (null != attrs)
      {
        foreach (var t in attrs)
        {
          var pair = t.Split('=');
          ret.Append(_GetTagAttributePattern(pair[0], pair[1]));
        }
      }
      ret.Append(_RB);
      return ret.ToString();
    }

    private static string _GetTagStartPatternNotStrictly(string tag)
    {
      var ret = new StringBuilder();
      ret.Append(_LB);
      ret.Append(_ZeroSpace(tag));
      ret.Append(_Value);
      ret.Append(_RB);
      return ret.ToString();

    }

    private static string _GetTagStopPattern(string tag)
    {
      return _LB + _ZeroSpace(@"\/") + tag + _RB;
    }

    private static void _OnTotalProgress()
    {
      if (null != TotalProgressed)
      {
        var pc = Math.Max(_LastPc, (100.0 * _CompletedResReq) / _AddedResReq);
        if (_LastPc != pc)
        {
          _LastPc = pc;
          TotalProgressed(pc);
        }
      }
    }

    public const int DefaultItemsInLists = 20;

    public static void IncAddedResReq()
    {
      Interlocked.Increment(ref _AddedResReq);
      _OnTotalProgress();
    }

    public static void IncCompletedResReq()
    {
      Interlocked.Increment(ref _CompletedResReq);
      _OnTotalProgress();
    }

    public static readonly Regex LinkIdPattern = new Regex(@"\d+", _PatternsOpt);

    public static int SoftIndexOf(this string txt, string pattern, int start)
    {
      return txt.IndexOf(pattern, start, StringComparison.InvariantCultureIgnoreCase);
    }

    //Паттерн элемента таблицы списка топиков вида 
    //<td class="lm"><a href='/forum/humour/5098305.flat#last'><img class=t src='images/fr0.gif' alt=' '></a></td>
    public static readonly Regex LinksListPattern = new Regex(
              _GetTagStartPattern("td", "class=lm") +
              _GetTagStartPattern("a", "href=" + _Reference) +
              _GetTagStartPatternNotStrictly("img") +
              _GetTagStopPattern("a") +
              _GetTagStopPattern("td"), _PatternsOpt);

    //Паттерн элемента счетчика рейтинговых позиций
    //<div style="font-size: smaller">
    //<script language="javascript" src="../script/showmenu.v1.js" type="text/javascript" charset="utf-8"></script>
    //<span>&nbsp;Top 100: e-Xecutor (всего сообщений - 38)</span>
    //</div>
    public static readonly Regex RatingListCountPlaceholderPattern = new Regex(
              _GetTagStartPatternNotStrictly("div") +
              _GetTagStartPatternNotStrictly("script") + _GetTagStopPattern("script") +
              _GetTagStartPatternNotStrictly("span") + _Value + _GetTagStopPattern("span") +
              _GetTagStopPattern("div"), _PatternsOpt);

    public static readonly Regex RatingListCountValue1Pattern = new Regex(
              _GetTagStartPatternNotStrictly("span") + _Value + _GetTagStopPattern("span"), _PatternsOpt);

    public static readonly Regex RatingListCountValue2Pattern = new Regex(
              _ZeroSpace(@"\(") + _Value + _ZeroSpace(@"\)"), _PatternsOpt);

    //Паттерн элемента таблицы списка оценок вида 
    //<td class="lm"><img class=t src='images/fr0.gif' alt=' '/></td>
    //<td style="width:100%;"><a href="/forum/humour/5096566.1" style="font-weight:bold;">Тени прошлого</a></td>
    //<td align="right">:)</td>
    //<td nowrap="nowrap" style="font-size:XX-Small;"><a href="/account/info/65674" target="_parent">Пофигист</a></td>
    //<td nowrap="nowrap" style="font-size:XX-Small;">13.03.13 10:05</td>
    public static readonly Regex RatingListPattern = new Regex(
              _GetTagStartPattern("td", "class=lm") + _GetTagStartPatternNotStrictly("img") + _GetTagStopPattern("td") +
              _GetTagStartPatternNotStrictly("td") + _GetTagStartPatternNotStrictly("a") + _Value + _GetTagStopPattern("a") + _GetTagStopPattern("td") +
              _GetTagStartPatternNotStrictly("td") + _Value + _GetTagStopPattern("td") +
              _GetTagStartPatternNotStrictly("td") + _GetTagStartPatternNotStrictly("a") + _Value + _GetTagStopPattern("a") + _GetTagStopPattern("td") +
              _GetTagStartPatternNotStrictly("td") + _Value + _GetTagStopPattern("td"), _PatternsOpt);

    public static readonly Regex HrefPattern = new Regex(_GetTagAttributePattern("href", _Reference), _PatternsOpt);

    public static readonly Regex RefPattern = new Regex(_Quote + _ZeroSpace(_Reference) + _ZeroSpace(_Quote), _PatternsOpt);

    public static readonly Regex SmileRatePattern = new Regex(
      _GetTagStartPatternNotStrictly("td") + @"(\:\))" + _GetTagStopPattern("td"), _PatternsOpt);

    public static readonly Regex MinusRatePattern = new Regex(
      _GetTagStartPatternNotStrictly("td") + @"\-" + _GetTagStopPattern("td"), _PatternsOpt);

    public static readonly Regex PlusRatePattern = new Regex(
      _GetTagStartPatternNotStrictly("td") + @"\+" + _GetTagStopPattern("td"), _PatternsOpt);

    public static readonly Regex NumRatePattern = new Regex(
      _GetTagStartPatternNotStrictly("td") + @"(\d+|(\d+\/\d+))" + _GetTagStopPattern("td"), _PatternsOpt);

    public static Uri ResDomain;

    public static event ProgressHandlerDelegate TotalProgressed;

    public static ResId GetIdFromAHref(string value)
    {
      var ms = ResReqHelper.HrefPattern.Matches(value);
      if (ms.Count != 1)
        throw new Exception(string.Format("<a href>: find {0} matches in {1}", ms.Count, value));
      return new ResId(ResReqHelper.RefPattern.Match(ms[0].Value).Value);
    }


    public static Statistics Statistics
    {
      get
      {
        if (null == _Statistics)
        {
          var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(null,
            new System.Runtime.Serialization.StreamingContext(System.Runtime.Serialization.StreamingContextStates.All));
          try
          {
            using (var f = File.Open(_StatDataFile, FileMode.Open, FileAccess.Read))
              _Statistics = (Statistics)formatter.Deserialize(f);
          }
          catch
          {
            _Statistics = new Statistics();
          }
        }
        return _Statistics;
      }
    }

    public static void ResetStat()
    {
      File.Delete(_StatDataFile);
      _Statistics = null;
    }

    public static void SaveStat()
    {
      if (null != _Statistics)
      {
        var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        using (var f = File.Open(_StatDataFile, FileMode.Create, FileAccess.Write))
            formatter.Serialize(f, _Statistics);
      }
    }
  }

}
